package com.hmdp.controller;


import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.entity.UserInfo;
import com.hmdp.service.IUserInfoService;
import com.hmdp.service.IUserService;
import com.hmdp.utils.SystemConstants;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;

    @Resource
    private IUserInfoService userInfoService;

    /**
     * 发送手机验证码
     *      -- 基于 Session 的验证码登录功能：
     *          -- 这种其实就是两个请求 第一个是来获取验证码的 第二次是前端带着验证码来登录 当前前端得知验证码是通过发短信的
     *          -- 第一次请求进来了先校验下手机号格式 因为用户可以绕过前端的校验 所以需要后端校验
     *          -- 后端接着以随机字符串的形式生成一个六位数验证码 并且通过第三方发送给用户手机上
     *          -- 那用户下次请求的时候带着验证码 后端怎么知道它对不对 ??? 因为两次请求之间是独立的
     *          -- 第二次请求不知道第一次生成的验证码 这里用 Session 来保存一个会话之间的数据
     *          -- 这个用户通过浏览器发起连接 从此他们通过 Cookie 来共享 Session
     *          -- 发送验证码的时候把验证码保存在 Session 中 下次请求的时候来取就行了
     *          -- 因为这个 Session 是这个用户独有的 所以键上面不用加用户独有的标识 直接用 code 做键就行了
     *      -- 基于 Redis 的验证码登录功能：
     *          -- 因为 Session 不能集群 因为多台 tomcat 上的 Session 不能共享 虽然有这个技术 但是性能不好
     *          -- 此时就需要一个中间层来保存数据 所有的都在 Redis 上面存 于是就脱离了 tomcat 能集群了
     *          -- 此时验证码业务的区别就是每次生成的验证码要往Redis中存了 而且此时区分不同的用户已经不会再由 tomcat 做好了
     *          -- 所以 key 里面要区分不同的用户 所以 key 要加上用户id 这里直接用 phone 做 key 拼接上业务前缀
     *          -- key 最好设置有效期 首先是本来就有时效性 而且一直存会占空间
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) throws Exception {
        // 发送短信验证码并保存验证码
        String code = userService.sendCode(phone, session);
        return Result.ok(code);
    }

    /**
     * 登录功能
     *      -- 基于 Session 的登录功能
     *      -- 前端传递过来手机号和验证码 后端校验格式后
     *      -- 从 Session 中取出验证码 做比对
     *      -- 接着根据传过来的手机号查询出用户信息存入 Session 方便后续用拦截器将用户信息存入 ThreadLocal
     *      -- 这里面前端提交手机号和验证码 后端通过手机号取出验证码做比对后 将用户信息存入 Redis 中
     *      -- 这里面每个用户也是存到时候要有一个自己的 key 这里面用令牌技术 每个请求携带一个令牌来获取用户信息
     * @param loginForm 登录参数，包含手机号、验证码；或者手机号、密码
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        // 实现登录功能
        log.info("用户登录 登录参数：{}", loginForm);
        String jwt = userService.login(loginForm, session) ;
        return Result.ok(jwt);
    }

    /**
     * 登出功能
     * @return 无
     */
    @PostMapping("/logout")
    public Result logout(HttpServletRequest request ,HttpSession session){
        // 实现登出功能
        log.info("用户登出...");
        session.removeAttribute(SystemConstants.USER_SIGN_KEY) ;
        userService.logout(request ,session) ;
        return Result.ok();
    }

    @GetMapping("/me")
    public Result me(){
        // 获取当前登录的用户并返回
        UserDTO user = UserHolder.getUser();
        return Result.ok(user);
    }

    @GetMapping("/info/{id}")
    public Result info(@PathVariable("id") Long userId){
        // 查询详情
        UserInfo info = userInfoService.getById(userId);
        if (info == null) {
            // 没有详情，应该是第一次查看详情
            return Result.ok();
        }
        info.setCreateTime(null);
        info.setUpdateTime(null);
        // 返回
        return Result.ok(info);
    }

    /**
     * 根据 id 查询用户信息
     * @param userId
     * @return
     */
    @GetMapping("/{id}")
    public Result queryUserById(@PathVariable("id") Long userId){
        // 查询详情
        User user = userService.getById(userId);
        if (user == null) {
            return Result.ok();
        }
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        // 返回
        return Result.ok(userDTO);
    }

    /**
     * 用户签到功能
     * @return
     */
    @PostMapping("/sign")
    public Result sign(){
        userService.sign();
        return Result.ok();
    }

    /**
     * 连续签到功能
     */
    @GetMapping("/sign/count")
    public Result signCount() {
        log.info("用户连续签到次数统计...");
        Integer count = userService.signCount() ;
        return Result.ok(count);
    }
}
